# @Time    : 2024/2/25 20:12
# @Author  : 🍁
# @File    : issues.py
# @Software: PyCharm
import datetime
import json
import time

from django.shortcuts import render, reverse
from django import http
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.safestring import mark_safe

from web import forms
from web.forms.issues import IssuesForm, InviteForms
from web import models
from web.utils.pagination import Pagination
from web.utils.scrypty import uid


# 单选框筛选
class CheckFilter():
    def __init__(self, request, name, check_list):
        self.reqeust = request
        self.name = name
        self.check_list = check_list

    def __iter__(self):
        for item in self.check_list:
            key = str(item[0])
            text = item[1]
            ck = ""
            value_list = self.reqeust.GET.getlist(self.name)
            if key in value_list:  # 判断key 是否在参数列表中
                ck = "checked"
                value_list.remove(key)  # 在就去除掉
            else:
                value_list.append(key)  # 不在就添加进来
            url_dict = self.reqeust.GET.copy()  # 拿到路径当中的参数，并复制一份，避免修改原来的路径
            url_dict._mutable = True
            # ?status=1&status=2&issues_type=1
            # url.setlist("issues", [1, 2, 3]) {"issues":[1, 2, 3]}
            # url_dict.urlencode() ?status=1&status=2&issues_type=1&issues_type=2&issues_type=3
            url_dict.setlist(self.name, value_list)
            if "page" in url_dict:  # 如果路径参数当中有page 就去掉page，相当于跳转到第一页在筛选
                url_dict.pop("page")
            param_url = url_dict.urlencode()
            if param_url:  # 如果有参数
                url = "{}?{}".format(self.reqeust.path_info, param_url)
            else:  # 如果没有参数就把路径中的? 去掉
                url = self.reqeust.path_info
            tpl = '<a class="cell" href="{url}"><input type="checkbox" {ck} /><label>{text}</label></a>'
            html = mark_safe(tpl.format(url=url, ck=ck, text=text))
            yield html


# 下拉框筛选
class SelectFilter():
    def __init__(self, reqeust, name, select_filed):
        self.reqeust = reqeust
        self.name = name
        self.select_filed = select_filed

    def __iter__(self):
        yield mark_safe("<select class='select2' multiple='multiple' style='width:100%;'>")
        for item in self.select_filed:
            key = str(item[0])
            text = item[1]
            ck = ""
            value_list = self.reqeust.GET.getlist(self.name)
            if key in value_list:
                ck = "selected"
                value_list.remove(key)  # 在就去除掉
            else:
                value_list.append(key)  # 不在就添加进来
            url_dict = self.reqeust.GET.copy()  # 拿到路径当中的参数，并复制一份，避免修改原来的路径
            url_dict._mutable = True
            # ?status=1&status=2&issues_type=1
            # url.setlist("issues", [1, 2, 3]) {"issues":[1, 2, 3]}
            # url_dict.urlencode() ?status=1&status=2&issues_type=1&issues_type=2&issues_type=3
            url_dict.setlist(self.name, value_list)
            if "page" in url_dict:  # 如果路径参数当中有page 就去掉page，相当于跳转到第一页在筛选
                url_dict.pop("page")
            param_url = url_dict.urlencode()
            if param_url:  # 如果有参数
                url = "{}?{}".format(self.reqeust.path_info, param_url)
            else:  # 如果没有参数就把路径中的? 去掉
                url = self.reqeust.path_info
            tpl = '<option value="{url}" {ck} >{text}</option>'
            html = mark_safe(tpl.format(url=url, ck=ck, text=text))
            yield html
        yield mark_safe("</select>")


# 问题主页
class IssuesView(View):

    def get(self, request, project_id):
        # 根据URL做筛选，筛选条件（根据用户通过GET传过来的参数实现）
        # ?status=1&status=2&issues_type=1
        allow_filter_name = ['issues_type', 'status', 'priority', 'assign', 'attention']
        condition = {}
        for name in allow_filter_name:
            value_list = request.GET.getlist(name)  # [1,2]
            if not value_list:
                continue
            condition["{}__in".format(name)] = value_list
        """
        condition = {
            "status__in":[1,2],
            'issues_type':[1,]
        }
        """

        # 分页展示
        issues_object = models.Issues.objects.filter(project=project_id).filter(**condition)
        page = Pagination(
            current_page=request.GET.get("page"),
            all_count=issues_object.count(),
            base_url=request.path_info,
            query_params=request.GET,
        )
        form = IssuesForm(request)
        issues_object_list = issues_object[page.start:page.end]

        # 拿到项目创建者
        creator_list = [(request.tartec.project.created_by_id, request.tartec.project.created_by.username)]
        attention_list = models.UserProject.objects.filter(project_id=project_id).values_list("user_id",
                                                                                              "user__username")
        creator_list.extend(attention_list)
        # 邀请码的表单
        invite_form = InviteForms()
        content = {"form": form,
                   "issues_object_list": issues_object_list,
                   "page_html": page.page_html(),
                   "filter_list": [
                       {"title": "问题类型",
                        "filter": CheckFilter(request, "issues_type", models.IssuesType.objects.filter(
                            project_id=project_id).values_list("id", "title"))},
                       {"title": "状态", "filter": CheckFilter(request, "status", models.Issues.status_choices)},
                       {"title": "优先级", "filter": CheckFilter(request, "priority", models.Issues.priority_choices)},
                       {"title": "指派者", "filter": SelectFilter(request, "assign", creator_list)},
                       {"title": "关注者", "filter": SelectFilter(request, "attention", creator_list)},
                   ],
                   "invite_form": invite_form
                   }
        return render(request, "issues.html", content)

    def post(self, request, project_id):
        form = IssuesForm(request, data=request.POST)
        if form.is_valid():
            # 保存添加信息
            form.instance.project = request.tartec.project
            form.instance.creator = request.tartec.user
            form.save()
            return http.JsonResponse({"status": True})
        return http.JsonResponse({"status": False, "error": form.errors})


# 问题操作页面
class IssuesDetailView(View):
    def get(self, request, project_id, issues_id):
        issues_object = models.Issues.objects.filter(id=issues_id, project=request.tartec.project).first()
        form = IssuesForm(request, instance=issues_object)
        return render(request, "issues_detail.html", {"form": form, "issues_object": issues_object})


# 问题操作记录
class IssuesRecordView(View):
    def get(self, request, project_id, issues_id):
        reply = models.RecordingModel.objects.filter(issues=issues_id, issues__project=request.tartec.project)
        data_list = []
        for row in reply:
            data = {
                "id": row.id,
                "status": row.get_status_display(),
                "action": row.action,
                "user": row.user.username,
                "parent": row.parent_id,
                "create_date": row.create_date.strftime("%Y-%m-%d %H:%M")
            }
            data_list.append(data)
        return http.JsonResponse({"status": True, "data": data_list})

    @csrf_exempt
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, request, project_id, issues_id):
        form = forms.issues.IssuesRecordingForm(request, request.POST)
        if form.is_valid():
            form.instance.issues_id = issues_id
            form.instance.user = request.tartec.user
            form.instance.status = 2
            instance = form.save()
            data = {
                "id": instance.id,
                "status": instance.get_status_display(),
                "action": instance.action,
                "user": instance.user.username,
                "parent": instance.parent_id,
                "create_date": instance.create_date.strftime("%Y-%m-%d %H:%M")
            }
            return http.JsonResponse({"status": True, "data": data})
        return http.JsonResponse({"status": False, "error": form.errors})


# 修改问题记录
class IssuesChangeRecordView(View):
    @csrf_exempt
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, request, project_id, issues_id):
        # 当前项目的当前问题
        issues_object = models.Issues.objects.filter(id=issues_id, project_id=project_id).first()
        change_message = json.loads(request.body)
        name = change_message.get("name")
        value = change_message.get("value")
        field = models.Issues._meta.get_field(change_message["name"])

        # 问题操作记录的模板
        def create_issues_record(content):
            # 返回更新信息
            update_msg = models.RecordingModel.objects.create(
                status=1,
                issues=issues_object,
                action=content,
                user=request.tartec.user
            )
            data = {
                "id": update_msg.id,
                "status": update_msg.get_status_display(),
                "action": update_msg.action,
                "user": update_msg.user.username,
                "parent": update_msg.parent_id,
                "create_date": update_msg.create_date.strftime("%Y-%m-%d %H:%M")
            }
            return data

        # 如果是文本信息
        if name in ["subject", "desc", "start_date", "end_date"]:
            if not value:
                if not field.null:  # 如果该字段不能为空，并且获取到的值为空时返回错误信息
                    return http.JsonResponse({"status": False, "error": "该字段不能为空"})
                else:  # 如果没有值，能为空时，就更新值为空
                    setattr(issues_object, name, None)
                    change_record = "{}变更为空".format(field.verbose_name)
                    issues_object.save()
            setattr(issues_object, name, value)
            change_record = "{}变更为{}".format(field.verbose_name, value)
            issues_object.save()
            # 返回更新信息
            return http.JsonResponse({"status": True, "data": create_issues_record(change_record)})

        # 如果是M2M信息
        elif name in ["attention"]:
            # 判断数据类型是否是{"name": xxx, "value": [1, 2, 3,]}
            if not isinstance(value, list):
                return http.JsonResponse({"status": False, "error": "数据格式错误"})
            if value:  # 判断value 是否有值
                # 把当前项目创建者放进字典
                user_dict = {str(request.tartec.user.id): request.tartec.user.username}
                # 拿到当前项目参与者
                user_object_list = models.UserProject.objects.filter(project_id=project_id)
                for item in user_object_list:
                    # 拿到该项目的所有成员(创建者，参与者)
                    user_dict[item.id] = item.user.username
                username_list = []  # 用户名列表
                for item in user_dict:
                    username = user_dict.get(str(item))
                    if not username:  # 判断该用户名是否存在
                        return http.JsonResponse({"status": False, "error": "用户不存在，请刷新后重试"})
                    username_list.append(username)
                issues_object.attention.set(value)
                issues_object.save()
                change_record = "{}变更为{}".format(field.verbose_name, ",".join(username_list))
            else:
                issues_object.attention.set([])  # 取消已经生成的关系
                issues_object.save()
                change_record = "{}变成为空".format(field.verbose_name)
            return http.JsonResponse({"status": True, "data": create_issues_record(change_record)})

        # 如果是FK信息
        elif name in ["issues_type", "module", "assign", "parent", ]:
            if not value:
                if not field.null:  # 如果该字段不能为空，并且获取到的值为空时返回错误信息
                    return http.JsonResponse({"status": False, "error": "该字段不能为空"})
                else:  # 如果没有值，能为空时，就更新值为空
                    setattr(issues_object, name, None)
                    issues_object.save()
                    change_record = "{}变更为空".format(field.verbose_name)
                    return http.JsonResponse({"status": True, "data": create_issues_record(change_record)})
            """
            field.rel.objects.filter() 1.9版本开始淘汰
            field.remote_field.model.objects.filter() 现在用的是这个
            这里相当于拿到 ["issues_type", "module", "assign", "parent", ] 里面关联的表对象
            """
            if name == "assign":  # 指派者关联的时用户表和其他的不一样
                if value == str(request.tartec.project.created_by_id):  # 判断是否是项目创建者
                    instance = request.tartec.project.created_by  # 如果是就赋值给instance
                else:  # 不是创建者，看是否是项目参与者
                    project_user_object = models.UserProject.objects.filter(id=value,
                                                                            project=request.tartec.project).first()
                    if project_user_object:  # 判断是否是参与者
                        instance = project_user_object  # 把参与者对象赋值给instance
                    else:
                        instance = None  # 不是项目参与者就赋值给空
                if instance is None:
                    return http.JsonResponse({"status": False, "error": "你选择的用户不存在"})
                change_record = "{}变更为{}".format(field.verbose_name, str(instance))
                setattr(issues_object, name, instance)
                issues_object.save()
            else:
                instance = field.remote_field.model.objects.filter(id=value, project=request.tartec.project).first()
                setattr(issues_object, name, instance)
                issues_object.save()
                # str(instance) 取决于models 创建的表里面的 __str__ 方法返回什么这里就是什么
                change_record = "{}变更为{}".format(field.verbose_name, str(instance))
                # 返回更新信息
            return http.JsonResponse({"status": True, "data": create_issues_record(change_record)})

        # 如果是choices信息
        elif name in ["priority", "status", "mode"]:
            select_text = None
            for key, text in field.choices:  # 拿到表中的choices 元组并循环
                if str(key) == value:  # 判断value 是否在元组中
                    select_text = text
            if select_text:
                change_record = "{}变成了{}".format(field.verbose_name, select_text)
                return http.JsonResponse({"status": True, "data": create_issues_record(change_record)})
            return http.JsonResponse({"status": False, "error": "该选择不存在"})

        # 如果都不是
        else:
            return http.JsonResponse({"status": False, "error": "What are you doing????"})


# 生成邀请码
class InviteView(View):
    def post(self, request, project_id):
        form = InviteForms(data=request.POST)
        if form.is_valid():
            if request.tartec.user != request.tartec.project.created_by:
                form.add_error("period", "没有权限此操作")
                return http.JsonResponse({"status": False})
            # 生成随机字符串
            random_str_invite_code = uid(request.tartec.user.mobile)
            form.instance.code = random_str_invite_code
            form.instance.project = request.tartec.project
            form.instance.creator = request.tartec.user
            form.save()
            # 返回一个邀请链接
            url = "{scheme}://{host}{code}".format(
                scheme=request.scheme,
                host=request.get_host(),
                code=reverse("invite_join", kwargs={"code": random_str_invite_code})
            )
            return http.JsonResponse({"status": True, "data": url})
        return http.JsonResponse({"status": False, "error": form.errors})


class InviteJoin(View):
    def get(self, request, code):
        invite_object = models.ProjectInvite.objects.filter(code=code).first()
        #  判断邀请码是否正确
        if not invite_object:
            return render(request, "invite_join.html", {"error": "邀请码错误"})

        # 判断是否是项目创建者
        creator_object = models.Project.objects.filter(created_by=request.tartec.user,
                                                       id=invite_object.project.id).first()
        if creator_object:
            return render(request, "invite_join.html", {"error": "项目创建者无需加入"})

        # 判断是否是项目成员
        assign = models.UserProject.objects.filter(user=request.tartec.user, project=request.tartec.project).first()
        if assign:
            return render(request, "invite_join.html", {"error": "邀请码错误"})

        # 拿到项目能邀请到的最大人数
        create_user_object = invite_object.project.created_by_id
        # classify = models.TransactionRecord.objects.filter(user=create_user_object).order_by("-id")
        # # 判断项目创建者的价格策略是否过期
        # if classify.price_classify.classify == 1:
        #     max_number = classify.price_classify.everyone_project_member
        # if datetime.datetime.now() > classify.end_date:
        #     free_classify = models.PriceClassify.objects.filter(classify=1).first()
        #     max_number = free_classify.everyone_project_member
        # else:
        #     max_number = classify.price_classify.everyone_project_member
        user_classify = models.User.objects.filter(id=create_user_object).first()
        classify = models.PriceClassify.objects.filter(classify=user_classify.price_classify.classify).first()
        max_number = classify.everyone_project_member

        # 拿到已经添加的人数
        invite_member_number = models.UserProject.objects.filter(project=invite_object.project).count()
        if max_number < invite_member_number:
            return render(request, "invite_join.html", {"error": "超过项目人数，请升级套餐"})

        # 判断邀请码是否过期
        # 拿到现在的时间
        now_time = datetime.datetime.now()
        # 拿到邀请过期的时间
        invite_time = invite_object.create_datetime + datetime.timedelta(minutes=invite_object.period)
        if now_time > invite_time:
            return render(request, "invite_join.html", {"error": "邀请码已过期"})

        if invite_object.count:
            # 判断是否超过项目邀请最大人数
            # 拿到可邀请的最大人数
            invite_max_number = invite_object.count
            # 拿到已经邀请的人数
            if invite_max_number < invite_member_number:
                return render(request, "invite_join.html", {"error": "超过了邀请人数限制"})
            else:
                invite_object.count += 1
                invite_object.save()
            models.UserProject.objects.create(
                user=request.tartec.user,
                project=request.tartec.project
            )

            return render(request, "invite_join.html", {"project": invite_object.project})
